using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

using ComTypes = System.Runtime.InteropServices.ComTypes;

namespace dtxCore.Dokan {
	[StructLayout(LayoutKind.Sequential, Pack = 4)]
	struct BY_HANDLE_FILE_INFORMATION {
		public uint dwFileAttributes;
		public ComTypes.FILETIME ftCreationTime;
		public ComTypes.FILETIME ftLastAccessTime;
		public ComTypes.FILETIME ftLastWriteTime;
		public uint dwVolumeSerialNumber;
		public uint nFileSizeHigh;
		public uint nFileSizeLow;
		public uint dwNumberOfLinks;
		public uint nFileIndexHigh;
		public uint nFileIndexLow;
	}

	[StructLayout(LayoutKind.Sequential, Pack = 4)]
	struct DOKAN_FILE_INFO {
		public ulong Context;
		public ulong DokanContext;
		public IntPtr DokanOptions;
		public uint ProcessId;
		public byte IsDirectory;
		public byte DeleteOnClose;
		public byte PagingIo;
		public byte SynchronousIo;
		public byte Nocache;
		public byte WriteToEndOfFile;
	}

	[Flags]
	enum SECURITY_INFORMATION : uint {
		OWNER_SECURITY_INFORMATION = 0x00000001,
		GROUP_SECURITY_INFORMATION = 0x00000002,
		DACL_SECURITY_INFORMATION = 0x00000004,
		SACL_SECURITY_INFORMATION = 0x00000008,
		UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
		UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
		PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
		PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
	}

	[StructLayoutAttribute(LayoutKind.Sequential, Pack = 4)]
	struct SECURITY_DESCRIPTOR {
		public byte revision;
		public byte size;
		public short control;
		public IntPtr owner;
		public IntPtr group;
		public IntPtr sacl;
		public IntPtr dacl;
	}

	class Proxy {
		private DokanOperations operations_;
		private ArrayList array_;
		private Dictionary<ulong, DokanFileInfo> infoTable_;
		private ulong infoId_ = 0;
		private object infoTableLock_ = new object();
		private DokanOptions options_;

		public Proxy(DokanOptions options, DokanOperations operations) {
			operations_ = operations;
			options_ = options;
			array_ = new ArrayList();
			infoTable_ = new Dictionary<ulong, DokanFileInfo>();
		}

		private void ConvertFileInfo(ref DOKAN_FILE_INFO rawInfo, DokanFileInfo info) {
			info.IsDirectory = rawInfo.IsDirectory == 1;
			info.ProcessId = rawInfo.ProcessId;
			info.PagingIo = rawInfo.PagingIo == 1;
			info.DeleteOnClose = rawInfo.DeleteOnClose == 1;
			info.SynchronousIo = rawInfo.SynchronousIo == 1;
			info.Nocache = rawInfo.Nocache == 1;
			info.WriteToEndOfFile = rawInfo.WriteToEndOfFile == 1;
		}

		private DokanFileInfo GetNewFileInfo(ref DOKAN_FILE_INFO rawFileInfo) {
			DokanFileInfo fileInfo = new DokanFileInfo(rawFileInfo.DokanContext);

			lock(infoTableLock_) {
				fileInfo.InfoId = ++infoId_;

				rawFileInfo.Context = fileInfo.InfoId;
				ConvertFileInfo(ref rawFileInfo, fileInfo);
				// to avoid GC
				infoTable_[fileInfo.InfoId] = fileInfo;
			}
			return fileInfo;
		}

		private DokanFileInfo GetFileInfo(ref DOKAN_FILE_INFO rawFileInfo) {
			DokanFileInfo fileInfo = null;
			lock(infoTableLock_) {
				if(rawFileInfo.Context != 0) {
					infoTable_.TryGetValue(rawFileInfo.Context, out fileInfo);
				}

				if(fileInfo == null) {
					// bug?
					fileInfo = new DokanFileInfo(rawFileInfo.DokanContext);
				}
				ConvertFileInfo(ref rawFileInfo, fileInfo);
			}
			return fileInfo;
		}

		private string GetFileName(IntPtr fileName) {
			return Marshal.PtrToStringAuto(fileName);
		}


		private const uint GENERIC_READ = 0x80000000;
		private const uint GENERIC_WRITE = 0x40000000;
		private const uint GENERIC_EXECUTE = 0x20000000;

		private const uint FILE_READ_DATA = 0x0001;
		private const uint FILE_READ_ATTRIBUTES = 0x0080;
		private const uint FILE_READ_EA = 0x0008;
		private const uint FILE_WRITE_DATA = 0x0002;
		private const uint FILE_WRITE_ATTRIBUTES = 0x0100;
		private const uint FILE_WRITE_EA = 0x0010;

		private const uint FILE_SHARE_READ = 0x00000001;
		private const uint FILE_SHARE_WRITE = 0x00000002;
		private const uint FILE_SHARE_DELETE = 0x00000004;

		private const uint CREATE_NEW = 1;
		private const uint CREATE_ALWAYS = 2;
		private const uint OPEN_EXISTING = 3;
		private const uint OPEN_ALWAYS = 4;
		private const uint TRUNCATE_EXISTING = 5;

		private const uint FILE_ATTRIBUTE_ARCHIVE = 0x00000020;
		private const uint FILE_ATTRIBUTE_ENCRYPTED = 0x00004000;
		private const uint FILE_ATTRIBUTE_HIDDEN = 0x00000002;
		private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
		private const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000;
		private const uint FILE_ATTRIBUTE_OFFLINE = 0x00001000;
		private const uint FILE_ATTRIBUTE_READONLY = 0x00000001;
		private const uint FILE_ATTRIBUTE_SYSTEM = 0x00000004;
		private const uint FILE_ATTRIBUTE_TEMPORARY = 0x00000100;


		public delegate int CreateFileDelegate(
			IntPtr rawFilName,
			uint rawAccessMode,
			uint rawShare,
			uint rawCreationDisposition,
			uint rawFlagsAndAttributes,
			ref DOKAN_FILE_INFO dokanFileInfo);

		public int CreateFileProxy(
			IntPtr rawFileName,
			uint rawAccessMode,
			uint rawShare,
			uint rawCreationDisposition,
			uint rawFlagsAndAttributes,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				DokanFileInfo info = GetNewFileInfo(ref rawFileInfo);

				FileAccess access = FileAccess.Read;
				FileShare share = FileShare.None;
				FileMode mode = FileMode.Open;
				FileOptions options = FileOptions.None;

				if((rawAccessMode & FILE_READ_DATA) != 0 && (rawAccessMode & FILE_WRITE_DATA) != 0) {
					access = FileAccess.ReadWrite;
				} else if((rawAccessMode & FILE_WRITE_DATA) != 0) {
					access = FileAccess.Write;
				} else {
					access = FileAccess.Read;
				}

				if((rawShare & FILE_SHARE_READ) != 0) {
					share = FileShare.Read;
				}

				if((rawShare & FILE_SHARE_WRITE) != 0) {
					share |= FileShare.Write;
				}

				if((rawShare & FILE_SHARE_DELETE) != 0) {
					share |= FileShare.Delete;
				}

				switch(rawCreationDisposition) {
					case CREATE_NEW:
						mode = FileMode.CreateNew;
						break;
					case CREATE_ALWAYS:
						mode = FileMode.Create;
						break;
					case OPEN_EXISTING:
						mode = FileMode.Open;
						break;
					case OPEN_ALWAYS:
						mode = FileMode.OpenOrCreate;
						break;
					case TRUNCATE_EXISTING:
						mode = FileMode.Truncate;
						break;
				}

				int ret = operations_.CreateFile(file, access, share, mode, options, info);

				if(info.IsDirectory)
					rawFileInfo.IsDirectory = 1;

				return ret;
			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -2;
			}

		}

		////

		public delegate int OpenDirectoryDelegate(
			IntPtr FileName,
			ref DOKAN_FILE_INFO FileInfo);

		public int OpenDirectoryProxy(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				DokanFileInfo info = GetNewFileInfo(ref rawFileInfo);
				return operations_.OpenDirectory(file, info);

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int CreateDirectoryDelegate(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int CreateDirectoryProxy(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				DokanFileInfo info = GetNewFileInfo(ref rawFileInfo);
				return operations_.CreateDirectory(file, info);

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int CleanupDelegate(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int CleanupProxy(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);
				return operations_.Cleanup(file, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int CloseFileDelegate(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int CloseFileProxy(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);
				DokanFileInfo info = GetFileInfo(ref rawFileInfo);

				int ret = operations_.CloseFile(file, info);

				rawFileInfo.Context = 0;

				lock(infoTableLock_) {
					infoTable_.Remove(info.InfoId);
				}
				return ret;

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int ReadFileDelegate(
			IntPtr rawFileName,
			IntPtr rawBuffer,
			uint rawBufferLength,
			ref uint rawReadLength,
			long rawOffset,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int ReadFileProxy(
			IntPtr rawFileName,
			IntPtr rawBuffer,
			uint rawBufferLength,
			ref uint rawReadLength,
			long rawOffset,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				byte[] buf = new Byte[rawBufferLength];

				uint readLength = 0;
				int ret = operations_.ReadFile(
					file, buf, ref readLength, rawOffset, GetFileInfo(ref rawFileInfo));
				if(ret == 0) {
					rawReadLength = readLength;
					Marshal.Copy(buf, 0, rawBuffer, (int)rawBufferLength);
				}
				return ret;

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int WriteFileDelegate(
			IntPtr rawFileName,
			IntPtr rawBuffer,
			uint rawNumberOfBytesToWrite,
			ref uint rawNumberOfBytesWritten,
			long rawOffset,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int WriteFileProxy(
			IntPtr rawFileName,
			IntPtr rawBuffer,
			uint rawNumberOfBytesToWrite,
			ref uint rawNumberOfBytesWritten,
			long rawOffset,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				Byte[] buf = new Byte[rawNumberOfBytesToWrite];
				Marshal.Copy(rawBuffer, buf, 0, (int)rawNumberOfBytesToWrite);

				uint bytesWritten = 0;
				int ret = operations_.WriteFile(
					file, buf, ref bytesWritten, rawOffset, GetFileInfo(ref rawFileInfo));
				if(ret == 0)
					rawNumberOfBytesWritten = bytesWritten;
				return ret;

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int FlushFileBuffersDelegate(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int FlushFileBuffersProxy(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);
				int ret = operations_.FlushFileBuffers(file, GetFileInfo(ref rawFileInfo));
				return ret;

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int GetFileInformationDelegate(
			IntPtr FileName,
			ref BY_HANDLE_FILE_INFORMATION HandleFileInfo,
			ref DOKAN_FILE_INFO FileInfo);

		public int GetFileInformationProxy(
			IntPtr rawFileName,
			ref BY_HANDLE_FILE_INFORMATION rawHandleFileInformation,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				//System.Runtime.InteropServices.Marshal.PtrToStringAuto(rawFileName)
				//string * file_name = rawFileName.ToPointer();
				string file = GetFileName(rawFileName);

				FileInformation fi = new FileInformation();

				int ret = operations_.GetFileInformation(file, fi, GetFileInfo(ref rawFileInfo));

				if(ret == 0) {
					rawHandleFileInformation.dwFileAttributes = (uint)fi.Attributes;

					rawHandleFileInformation.ftCreationTime.dwHighDateTime =
						(int)(fi.CreationTime.ToFileTime() >> 32);
					rawHandleFileInformation.ftCreationTime.dwLowDateTime =
						(int)(fi.CreationTime.ToFileTime() & 0xffffffff);

					rawHandleFileInformation.ftLastAccessTime.dwHighDateTime =
						(int)(fi.LastAccessTime.ToFileTime() >> 32);
					rawHandleFileInformation.ftLastAccessTime.dwLowDateTime =
						(int)(fi.LastAccessTime.ToFileTime() & 0xffffffff);

					rawHandleFileInformation.ftLastWriteTime.dwHighDateTime =
						(int)(fi.LastWriteTime.ToFileTime() >> 32);
					rawHandleFileInformation.ftLastWriteTime.dwLowDateTime =
						(int)(fi.LastWriteTime.ToFileTime() & 0xffffffff);

					rawHandleFileInformation.nFileSizeLow =
						(uint)(fi.Length & 0xffffffff);
					rawHandleFileInformation.nFileSizeHigh =
						(uint)(fi.Length >> 32);
				}

				return ret;

			} catch(Exception e) {
				//MessageBox.Show(e.ToString());
				return -1;
			}

		}

		////

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
		struct WIN32_FIND_DATA {
			public FileAttributes dwFileAttributes;
			public ComTypes.FILETIME ftCreationTime;
			public ComTypes.FILETIME ftLastAccessTime;
			public ComTypes.FILETIME ftLastWriteTime;
			public uint nFileSizeHigh;
			public uint nFileSizeLow;
			public uint dwReserved0;
			public uint dwReserved1;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
			public string cFileName;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
			public string cAlternateFileName;
		}

		private delegate int FILL_FIND_DATA(
			ref WIN32_FIND_DATA rawFindData,
			ref DOKAN_FILE_INFO rawFileInfo);

		public delegate int FindFilesDelegate(
			IntPtr rawFileName,
			IntPtr rawFillFindData, // function pointer
			ref DOKAN_FILE_INFO rawFileInfo);

		public int FindFilesProxy(
			IntPtr rawFileName,
			IntPtr rawFillFindData, // function pointer
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				ArrayList files = new ArrayList();
				int ret = operations_.FindFiles(file, files, GetFileInfo(ref rawFileInfo));

				FILL_FIND_DATA fill = (FILL_FIND_DATA)Marshal.GetDelegateForFunctionPointer(
					rawFillFindData, typeof(FILL_FIND_DATA));

				if(ret == 0) {
					IEnumerator entry = files.GetEnumerator();
					while(entry.MoveNext()) {
						FileInformation fi = (FileInformation)(entry.Current);
						WIN32_FIND_DATA data = new WIN32_FIND_DATA();
						//ZeroMemory(&data, sizeof(WIN32_FIND_DATAW));

						data.dwFileAttributes = fi.Attributes;

						data.ftCreationTime.dwHighDateTime =
							(int)(fi.CreationTime.ToFileTime() >> 32);
						data.ftCreationTime.dwLowDateTime =
							(int)(fi.CreationTime.ToFileTime() & 0xffffffff);

						data.ftLastAccessTime.dwHighDateTime =
							(int)(fi.LastAccessTime.ToFileTime() >> 32);
						data.ftLastAccessTime.dwLowDateTime =
							(int)(fi.LastAccessTime.ToFileTime() & 0xffffffff);

						data.ftLastWriteTime.dwHighDateTime =
							(int)(fi.LastWriteTime.ToFileTime() >> 32);
						data.ftLastWriteTime.dwLowDateTime =
							(int)(fi.LastWriteTime.ToFileTime() & 0xffffffff);

						data.nFileSizeLow =
							(uint)(fi.Length & 0xffffffff);
						data.nFileSizeHigh =
							(uint)(fi.Length >> 32);

						data.cFileName = fi.FileName;

						fill(ref data, ref rawFileInfo);
					}

				}
				return ret;

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}

		}

		////

		public delegate int SetEndOfFileDelegate(
			IntPtr rawFileName,
			long rawByteOffset,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int SetEndOfFileProxy(
			IntPtr rawFileName,
			long rawByteOffset,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				return operations_.SetEndOfFile(file, rawByteOffset, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {

				MessageBox.Show(e.ToString());
				return -1;
			}
		}


		public delegate int SetAllocationSizeDelegate(
			IntPtr rawFileName,
			long rawLength,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int SetAllocationSizeProxy(
			IntPtr rawFileName,
			long rawLength,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				return operations_.SetAllocationSize(file, rawLength, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {

				MessageBox.Show(e.ToString());
				return -1;
			}
		}


		////

		public delegate int SetFileAttributesDelegate(
			IntPtr rawFileName,
			uint rawAttributes,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int SetFileAttributesProxy(
			IntPtr rawFileName,
			uint rawAttributes,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				FileAttributes attr = (FileAttributes)rawAttributes;
				return operations_.SetFileAttributes(file, attr, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int SetFileTimeDelegate(
			IntPtr rawFileName,
			ref ComTypes.FILETIME rawCreationTime,
			ref ComTypes.FILETIME rawLastAccessTime,
			ref ComTypes.FILETIME rawLastWriteTime,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int SetFileTimeProxy(
			IntPtr rawFileName,
			ref ComTypes.FILETIME rawCreationTime,
			ref ComTypes.FILETIME rawLastAccessTime,
			ref ComTypes.FILETIME rawLastWriteTime,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				long time;

				time = ((long)rawCreationTime.dwHighDateTime << 32) + (uint)rawCreationTime.dwLowDateTime;
				DateTime ctime = DateTime.FromFileTime(time);

				if(time == 0)
					ctime = DateTime.MinValue;

				time = ((long)rawLastAccessTime.dwHighDateTime << 32) + (uint)rawLastAccessTime.dwLowDateTime;
				DateTime atime = DateTime.FromFileTime(time);

				if(time == 0)
					atime = DateTime.MinValue;

				time = ((long)rawLastWriteTime.dwHighDateTime << 32) + (uint)rawLastWriteTime.dwLowDateTime;
				DateTime mtime = DateTime.FromFileTime(time);

				if(time == 0)
					mtime = DateTime.MinValue;

				return operations_.SetFileTime(
					file, ctime, atime, mtime, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int DeleteFileDelegate(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int DeleteFileProxy(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);

				return operations_.DeleteFile(file, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int DeleteDirectoryDelegate(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int DeleteDirectoryProxy(
			IntPtr rawFileName,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);
				return operations_.DeleteDirectory(file, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int MoveFileDelegate(
			IntPtr rawFileName,
			IntPtr rawNewFileName,
			int rawReplaceIfExisting,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int MoveFileProxy(
			IntPtr rawFileName,
			IntPtr rawNewFileName,
			int rawReplaceIfExisting,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);
				string newfile = GetFileName(rawNewFileName);

				return operations_.MoveFile(
					file, newfile, rawReplaceIfExisting != 0 ? true : false,
					GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int LockFileDelegate(
			IntPtr rawFileName,
			long rawByteOffset,
			long rawLength,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int LockFileProxy(
			IntPtr rawFileName,
			long rawByteOffset,
			long rawLength,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);
				return operations_.LockFile(
					file, rawByteOffset, rawLength, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int UnlockFileDelegate(
			IntPtr rawFileName,
			long rawByteOffset,
			long rawLength,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int UnlockFileProxy(
			IntPtr rawFileName,
			long rawByteOffset,
			long rawLength,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				string file = GetFileName(rawFileName);
				return operations_.UnlockFile(
					file, rawByteOffset, rawLength, GetFileInfo(ref rawFileInfo));

			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		////

		public delegate int GetDiskFreeSpaceDelegate(
			ref ulong rawFreeBytesAvailable,
			ref ulong rawTotalNumberOfBytes,
			ref ulong rawTotalNumberOfFreeBytes,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int GetDiskFreeSpaceProxy(
			ref ulong rawFreeBytesAvailable,
			ref ulong rawTotalNumberOfBytes,
			ref ulong rawTotalNumberOfFreeBytes,
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				return operations_.GetDiskFreeSpace(
					ref rawFreeBytesAvailable,
					ref rawTotalNumberOfBytes,
					ref rawTotalNumberOfFreeBytes,
					GetFileInfo(ref rawFileInfo));
			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		public delegate int GetVolumeInformationDelegate(
			IntPtr rawVolumeNameBuffer,
			uint rawVolumeNameSize,
			ref uint rawVolumeSerialNumber,
			ref uint rawMaximumComponentLength,
			ref uint rawFileSystemFlags,
			IntPtr rawFileSystemNameBuffer,
			uint rawFileSystemNameSize,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int GetVolumeInformationProxy(
			IntPtr rawVolumeNameBuffer,
			uint rawVolumeNameSize,
			ref uint rawVolumeSerialNumber,
			ref uint rawMaximumComponentLength,
			ref uint rawFileSystemFlags,
			IntPtr rawFileSystemNameBuffer,
			uint rawFileSystemNameSize,
			ref DOKAN_FILE_INFO FileInfo) {
			byte[] volume = System.Text.Encoding.Unicode.GetBytes(options_.VolumeLabel);
			Marshal.Copy(volume, 0, rawVolumeNameBuffer, Math.Min((int)rawVolumeNameSize, volume.Length));
			rawVolumeSerialNumber = 0x19831116;
			rawMaximumComponentLength = 256;

			// FILE_CASE_SENSITIVE_SEARCH | 
			// FILE_CASE_PRESERVED_NAMES |
			// FILE_UNICODE_ON_DISK
			rawFileSystemFlags = 7;

			byte[] sys = System.Text.Encoding.Unicode.GetBytes("DOKAN");
			Marshal.Copy(sys, 0, rawFileSystemNameBuffer, Math.Min((int)rawFileSystemNameSize, sys.Length));
			return 0;
		}


		public delegate int UnmountDelegate(
			ref DOKAN_FILE_INFO rawFileInfo);

		public int UnmountProxy(
			ref DOKAN_FILE_INFO rawFileInfo) {
			try {
				return operations_.Unmount(GetFileInfo(ref rawFileInfo));
			} catch(Exception e) {
				MessageBox.Show(e.ToString());
				return -1;
			}
		}

		public delegate int GetFileSecurityDelegate(
			IntPtr rawFileName,
			ref SECURITY_INFORMATION rawRequestedInformation,
			ref SECURITY_DESCRIPTOR rawSecurityDescriptor,
			uint rawSecurityDescriptorLength,
			ref uint rawSecurityDescriptorLengthNeeded,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int GetFileSecurity(
			IntPtr rawFileName,
			ref SECURITY_INFORMATION rawRequestedInformation,
			ref SECURITY_DESCRIPTOR rawSecurityDescriptor,
			uint rawSecurityDescriptorLength,
			ref uint rawSecurityDescriptorLengthNeeded,
			ref DOKAN_FILE_INFO rawFileInfo) {
			return -1;
		}

		public delegate int SetFileSecurityDelegate(
			IntPtr rawFileName,
			ref SECURITY_INFORMATION rawSecurityInformation,
			ref SECURITY_DESCRIPTOR rawSecurityDescriptor,
			uint rawSecurityDescriptorLength,
			ref DOKAN_FILE_INFO rawFileInfo);

		public int SetFileSecurity(
			IntPtr rawFileName,
			ref SECURITY_INFORMATION rawSecurityInformation,
			ref SECURITY_DESCRIPTOR rawSecurityDescriptor,
			ref uint rawSecurityDescriptorLengthNeeded,
			ref DOKAN_FILE_INFO rawFileInfo) {
			return -1;
		}
	}
}
